package gui;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

import app.AppInterface;

@SuppressWarnings("serial")
public class GuiLog extends JPanel implements ComponentListener {

	private final boolean DEBUG = false;

	private JScrollPane scrollPane;
	private JTextArea logTextArea;
	private JCheckBox saveLogCheckBox;
	private JButton clearLogButton;
	private AppInterface app;
	private boolean appendNewLines = false;

	public GuiLog(AppInterface app) {

		this.app = app;
		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));


		this.addComponentListener(this);


		logTextArea = new JTextArea(20, 40);
		scrollPane = new JScrollPane(logTextArea,
				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		logTextArea.setEditable(false);


		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));

		saveLogCheckBox = new JCheckBox(" Save Log History");
		saveLogCheckBox.setMnemonic('H');
		saveLogCheckBox.addItemListener(new SaveLogListener());

		clearLogButton = new JButton("Clear Log");
		clearLogButton.setMnemonic('C');
		clearLogButton.addActionListener(new ClearLogButtonListener());

		buttonPanel.add(saveLogCheckBox);
		buttonPanel.add(Box.createRigidArea(new Dimension(12, 0)));
		buttonPanel.add(clearLogButton);
		buttonPanel.add(Box.createHorizontalGlue());


		add(scrollPane);
		add(Box.createRigidArea(new Dimension(0, 5)));
		add(buttonPanel);
		add(Box.createRigidArea(new Dimension(0, 5)));
	}


	// Is called to initiated the log tab
	public void setGuiLog() {

		if(DEBUG) System.out.println("Initialize gui log");

		saveLogCheckBox.setSelected(app.getSaveLog());
	}


	// Function to add all the text to the GuiLog from log.txt
	private void loadLogFile() {
		this.getParent().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); // Sets the cursor to the loading cursor
		app.openLogFileForReading();
		String tempLine;
		int noOfLines = 0;
		while( (tempLine = app.getLogLine()) != null ) {
			// Exit the loop if there is something wrong with the log.txt file
			if(tempLine.equals("Error reading from log file")) {
				logTextArea.append(tempLine + "\n");
				break;
			}
			if(++noOfLines > 50000) { // Make sure that we don't run out of memory
				logTextArea.setText("OutOfMemoryError\nLog file is too big!\nPress \"Clear Log\" to empty the log file\n");
				Runtime.getRuntime().gc();
				break;
			}
			/*
			System.out.println("Total: " + Runtime.getRuntime().totalMemory() + " Max: " + Runtime.getRuntime().maxMemory() + " Free: " + Runtime.getRuntime().freeMemory());
			if(Runtime.getRuntime().totalMemory() > 0.9*Runtime.getRuntime().maxMemory()) { // TODO Try with a more complex algorithm
				if(Runtime.getRuntime().freeMemory() < 10000000L) {
					logTextArea.setText("OutOfMemoryError\nLog file is too big!\nPress \"Clear Log\" to empty the log file\n");
					Runtime.getRuntime().gc();
					break;
				}
			}
			 */
			logTextArea.append(tempLine + "\n");
		}
		appendNewLines = true;
		app.closeLogFileReadingPart();
		repaintTextAreaAndScroll();
		this.getParent().setCursor(Cursor.getDefaultCursor()); // Sets the cursor to the standard cursor
	}

	private void appendGuiLog(String logLine) {
		if(appendNewLines) {
			// Determine whether the scrollbar is currently at the very bottom position.
			JScrollBar vbar = scrollPane.getVerticalScrollBar();
			boolean autoScroll = ((vbar.getValue() + vbar.getVisibleAmount() + 128) > vbar.getMaximum());			

			logTextArea.append(logLine);

			if( autoScroll ) {
				logTextArea.setCaretPosition( logTextArea.getDocument().getLength() );
				repaint();
			}
		}
	}

	// Repaints the text area and scrolls down to the end of the text area
	private void repaintTextAreaAndScroll() {
		logTextArea.setCaretPosition( logTextArea.getDocument().getLength() );
		repaint();
	}

	private class SaveLogListener implements ItemListener {

		public void itemStateChanged(ItemEvent event) {
			if(!saveLogCheckBox.isSelected()) {
				int option = JOptionPane.showConfirmDialog(
						null,
						"This option will clear the log file.\nDo you want to proceed?",
						"Sync IT - Clear Log?",
						JOptionPane.YES_NO_OPTION);
				if(option != 0) {
					saveLogCheckBox.setSelected(true);
				}
			}

			app.setSaveLog(saveLogCheckBox.isSelected());
		}

	}

	private class ClearLogButtonListener implements ActionListener {

		public void actionPerformed(ActionEvent event) {
			app.clearLog();
			logTextArea.setText(null);
			repaintTextAreaAndScroll();
		}
	}


	// ComponentListener methods //

	public void componentShown(ComponentEvent event) {
		loadLogFile();
	}

	public void componentHidden(ComponentEvent event) {
		appendNewLines = false;
		logTextArea.setText(null);
		Runtime.getRuntime().gc();
	}

	public void componentMoved(ComponentEvent arg0) {
	}

	public void componentResized(ComponentEvent arg0) {
	}



	// Methods that comes from GuiChangeObserver, sent through the mainGui //

	// Enables and disables components when a sync is initiated or finished
	// is called through an observer
	public void enableGuiComponents(boolean show) {
		saveLogCheckBox.setEnabled(show);
		clearLogButton.setEnabled(show);
	}



	// Methods that comes from LogToGuiObserver, sent through the mainGui //

	public void connectionStatusWarning(String message) {
		message = message.split(";")[1] + " - " + message.split(";")[0];
		appendGuiLog(message + "\n"); //TODO Can miss to print messages, maybe receive all messages from log instead
	}

	public void transferMessage(String message) {
		// Simple filter
		if (!message.startsWith("Gathering data")) {
			message = message.split(";")[1] + " - " + message.split(";")[0];
			appendGuiLog(message + "\n"); //TODO Can miss to print messages, maybe receive all messages from log instead
		}
	}
}
